001    package net.sf.xdc.util;
002    
003    /*
004     *  Copyright 2005-2006 Jens Voß.
005     *
006     *  Licensed under the GNU Lesser General Public License (the "License");
007     *  you may not use this file except in compliance with the License.
008     *  You may obtain a copy of the License at
009     *
010     *       http://opensource.org/licenses/lgpl-license.php
011     *
012     *  Unless required by applicable law or agreed to in writing, software
013     *  distributed under the License is distributed on an "AS IS" BASIS,
014     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     *  See the License for the specific language governing permissions and
016     *  limitations under the License.
017     */
018    
019    import java.io.IOException;
020    import java.io.InputStream;
021    import java.io.Writer;
022    import java.util.Iterator;
023    import java.util.SortedSet;
024    import java.util.StringTokenizer;
025    import java.util.TreeSet;
026    import javax.xml.parsers.DocumentBuilderFactory;
027    import javax.xml.parsers.ParserConfigurationException;
028    
029    import org.apache.xml.serialize.DOMSerializer;
030    import org.apache.xml.serialize.OutputFormat;
031    import org.apache.xml.serialize.Serializer;
032    import org.apache.xml.serialize.SerializerFactory;
033    import org.w3c.dom.Document;
034    import org.w3c.dom.Element;
035    import org.w3c.dom.NamedNodeMap;
036    import org.w3c.dom.Node;
037    import org.w3c.dom.NodeList;
038    import org.xml.sax.InputSource;
039    import org.xml.sax.SAXException;
040    
041    /**
042     * This is a utility class responsible for parsing and printing XML documents.
043     *
044     * @author Jens Voß
045     * @since 0.5
046     * @version 0.5
047     */
048    public class XmlUtils {
049    
050      private static final OutputFormat OF_XML = new OutputFormat();
051      private static final OutputFormat OF_HTML = new OutputFormat();
052      private static final DocumentBuilderFactory FACTORY = DocumentBuilderFactory.newInstance();
053    
054      static {
055        OF_XML.setIndenting(true);
056        OF_XML.setIndent(2);
057        OF_XML.setEncoding("ISO-8859-1");
058        OF_XML.setPreserveSpace(false);
059        OF_HTML.setIndenting(true);
060        OF_HTML.setIndent(2);
061        OF_HTML.setEncoding("ISO-8859-1");
062        OF_HTML.setPreserveSpace(false);
063        OF_HTML.setStandalone(true);
064      }
065    
066      /**
067       * This method creates a new DOM document.
068       *
069       * @return A new DOM document
070       * @throws XmlException
071       */
072      public static Document createDocument () throws XmlException {
073        try {
074          return FACTORY.newDocumentBuilder().newDocument();
075        }
076        catch (ParserConfigurationException e) {
077          throw new XmlException(e);
078        }
079      }
080    
081      private static DOMSerializer getDOMSerializer (Writer out,
082                                                     String method) throws IOException {
083        if ("html".equals(method)) {
084          return new XdcSerializer(out, OF_HTML);
085        }
086        // otherwise, i.e. for non-html output formats:
087        SerializerFactory sf = SerializerFactory.getSerializerFactory(method);
088        Serializer ser = sf.makeSerializer(out, OF_XML);
089        return ser.asDOMSerializer();
090      }
091    
092      private static void print (Writer out,
093                                 Document doc,
094                                 String method) throws IOException {
095        getDOMSerializer (out, method).serialize(doc);
096      }
097    
098      /**
099       * This method streams an XML document with method="html" into a
100       * <code>Writer</code> object.
101       *
102       * @param out The <code>Writer</code> object to which the contents of the
103       *        document are written.
104       * @param doc The XML document to be streamed to the <code>Writer</code>
105       * @throws IOException
106       */
107      public static void printHtml (Writer out,
108                                    Document doc) throws IOException {
109        print(out, doc, "html");
110      }
111    
112      /**
113       * This method streams an XML document with method="xml" into a
114       * <code>Writer</code> object.
115       *
116       * @param out The <code>Writer</code> object to which the contents of the
117       *        document are written.
118       * @param doc The XML document to be streamed to the <code>Writer</code>
119       * @throws IOException
120       */
121      public static void printXml (Writer out,
122                                   Document doc) throws IOException {
123        print(out, doc, "xml");
124      }
125    
126      /**
127       * This method creates an XML document from the contents of an
128       * <code>InputStream</code>.
129       *
130       * @param in The <code>InputStream</code> parsed by this method
131       * @return An XML <code>Document</code> object with the contents of the
132       *         <code>InputStream</code>
133       * @throws XmlException
134       */
135      public static Document parse(InputStream in) throws XmlException {
136        try {
137          InputSource source = new InputSource(in);
138          return FACTORY.newDocumentBuilder().parse(source);
139        }
140        catch (SAXException e) {
141          throw new XmlException(e);
142        }
143        catch (IOException e) {
144          throw new XmlException(e);
145        }
146        catch (ParserConfigurationException e) {
147          throw new XmlException(e);
148        }
149      }
150    
151      /**
152       * This method is the Java equivalent to the template with
153       * <code>mode="util.getLink"</code> defined in the <code>util.xsl</code>
154       * stylesheet. It creates a name for an XML element which can be used for
155       * interlinking parts of the documentation. Unlike the links between
156       * element summary descriptions and their detailed descriptions (which are
157       * created by simply numbering the nodes in the order of occurrence),
158       * this template creates a name from all the element's attributes by
159       * concatenating the element name with a sequence of all attribute names
160       * and values, separated by a tilde (~), with the attribute names sorted
161       * alphabetically and blanks in the attribute values omitted.<br/>
162       * Example: The return value of this template for a the element<br/>
163       * <code>   <myNode yourAttr="attr one" ourAttr="attr two"/></code><br/>
164       * is
165       * <code>   myNode~outAttr~attrtwo~yourAttr~attrone</code>
166       *
167       * @param node The DOM <code>Node</code> for which the link name is
168       *        to be constructed
169       * @return The link name of the given DOM <code>Node</code>
170       */
171      public static String getLink(Element node) {
172        StringBuffer retVal = new StringBuffer(node.getNodeName());
173        StringBuffer attributeString = new StringBuffer();
174        NamedNodeMap attributes = node.getAttributes();
175        SortedSet attributeStrings = new TreeSet();
176        int num = attributes.getLength();
177        for (int i = 0; i < num; i++) {
178          Node attribute = attributes.item(i);
179          attributeString.setLength(0);
180          attributeString.append(attribute.getNodeName()).append('~');
181          String val = attribute.getNodeValue();
182          for (StringTokenizer tok = new StringTokenizer(val, " ", false); tok.hasMoreTokens();) {
183            attributeString.append(tok.nextToken());
184          }
185          attributeStrings.add(attributeString.toString());
186        }
187        for (Iterator iterator = attributeStrings.iterator(); iterator.hasNext();) {
188          retVal.append('~').append((String) iterator.next());
189        }
190        return retVal.toString();
191      }
192    
193      /**
194       * This utility method retrieves the text value from a DOM <code>Node</code>.
195       *
196       * @param node The node whose text value is to be retrieved
197       * @return The given <code>Node</code>'s text value
198       */
199      public static String getTextValue(Node node) {
200        StringBuffer retVal = new StringBuffer();
201        NodeList children = node.getChildNodes();
202        for (int i = 0; i < children.getLength(); i++) {
203          retVal.append(children.item(i).getNodeValue());
204        }
205        return retVal.toString();
206      }
207    
208      private XmlUtils() {
209      }
210    
211    }